import asyncio
import json
from typing import Any

from zai import ZhipuAiClient
try:
    from configs import GLM_API_KEY
except ImportError:
    GLM_API_KEY = "fabc0f1f271448f9a0b7207ae47f77c5.o8ITXRoUG1N1dTEe"


class MeetingContentProcessor:
    """对外统一入口：接入音频处理器，返回文本、摘要和关键词。"""

    def __init__(self):
        """
        :param audio_processor: 需实现 process_audio(audio_input) -> str 的对象
        """
        self.client = ZhipuAiClient(api_key=GLM_API_KEY)

    def call_glm_api(self, messages, json_mode=False) -> str:
        """调用GLM API"""
        try:
            response = self.client.chat.completions.create(
                model="glm-4.5",
                messages=messages,
                thinking={"type": "enabled"},
                response_format={"type": "json_object"} if json_mode else None
            )
            
            content: str = response.choices[0].message.content

            return content
            
        except Exception as e:
            print(f"调用GLM API失败：{str(e)}")
            return ""

    def summary_content(self, content) -> str:
        # prompt = """
        # 你是一个专业的会议总结专家，擅长从会议内容中总结出会议的要点和结论。
        # 请根据会议内容，总结出会议的要点和结论，包括：
        # 1. 会议主题
        # 2. 主要议题
        # 3. 关键决策
        # 4. 行动计划
        # 5. 注意事项
        
        # 请以结构化的JSON格式返回结果,其中包含一个meeting_summary字段，字段值为总结出的会议的要点和结论。
        # """
        prompt = """
        你是一个专业的会议记录分析助手。你的任务是从一段班前会的语音转录文本中，提取关键信息，并严格按照指定的JSON格式进行结构化输出。
请注意：
1. 输入文本是语音转录，可能存在错别字或不通顺的地方（如“装态”应为“状态”，“问件”应为“文件”）。请尽力根据上下文推断正确的含义。
2. 并非所有结构化字段都会在文本中出现。如果某个部分或字段在文本中没有提及，请将其值设为空字符串 "" 或 null，不要编造内容。
3. 你的最终输出必须是一个单一、合法的JSON对象，不要包含任何JSON之外的文字、解释或标记。
需要填充的json结构如下：
{
  "组织会前点名": {
    "组织人员": "",
    "点名情况": []
  },
  "检查与会人员状态": {
    "检查到的状态": [
      {
        "人员": "",
        "状况": "",
        "管理措施": "",
        "帮扶人员": ""
      }
    ]
  },
  "安排部署任务": {
    "传达精神": "",
    "任务布置": [
      {
        "负责组": "",
        "任务内容": ""
      }
    ]
  },
  "组织教育培训": {
    "安全注意事项": [],
    "隐患与应急措施": []
  },
  "强调注意事项": {
    "应知应会抽查": [
      {
        "抽查对象": "",
        "问题": "",
        "回答": ""
      }
    ],
    "事故案例警示": ""
  }
}"""
        messages = [
            {"role": "system", "content": prompt},
            {"role": "user", "content": content}
        ]
        return self.call_glm_api(messages, json_mode=True)

    def get_keywords(self, content) -> str:
        prompt = """
        你是一个专业的会议总结专家，擅长从会议内容中总结出会议的若干关键词。
        请根据会议内容，总结出会议的若干关键词，反馈结果为一个json，其中包含一个keywords字段，字段值为关键词列表。
        """
        messages = [
            {"role": "system", "content": prompt},
            {"role": "user", "content": content}
        ]
        return self.call_glm_api(messages, json_mode=True)

    async def process(self, audio_text: str) -> dict[str, Any]:
        """
        :param audio_input: 传递给音频处理器的输入（如文件路径/字节流等）
        :return: {"text": str, "summary": Any, "keywords": Any}
        """
        # get_keywords/summary_content 为同步方法，使用线程并发执行
        summary, keywords = await asyncio.gather(
            asyncio.to_thread(self.summary_content, audio_text),
            asyncio.to_thread(self.get_keywords, audio_text),
        )

        def _safe_parse(obj: Any) -> Any:
            if isinstance(obj, (dict, list)):
                return obj
            if isinstance(obj, str):
                s = obj.strip()
                if not s:
                    return {}
                try:
                    return json.loads(s)
                except Exception:
                    # 非JSON字符串，直接返回原文
                    return s
            return {}

        summary_parsed = _safe_parse(summary)
        keywords_parsed = _safe_parse(keywords)

        summary_obj = None
        if isinstance(summary_parsed, dict):
            summary_obj = summary_parsed
        else:
            try:
                summary_obj = json.loads(summary_parsed)
            except Exception:
                pass

        keywords_obj = None
        if isinstance(keywords_parsed, dict):
            kw = keywords_parsed.get("keywords")
            if isinstance(kw, list):
                keywords_obj = kw
            elif isinstance(kw, str) and kw.strip():
                try:
                    parsed_kw = json.loads(kw)
                    if isinstance(parsed_kw, list):
                        keywords_obj = parsed_kw
                except Exception:
                    pass
        elif isinstance(keywords_parsed, list):
            keywords_obj = keywords_parsed

        return {
            "text": audio_text,
            "summary": summary_obj,
            "keywords": keywords_obj,
        }

meeting_content_processor = MeetingContentProcessor()


if __name__ == "__main__":
    test_content = """嗯嗯9月18号，耶嗯第一个我们公司生产调度室夜班交接班会，嗯31个船，现在那个商船啊把10里坡全部倒走了，现在清场去，嗯现在从窗户那边一直没装满，一下子船技术岗去，完了现在清场清这个刚到这边就可以全部上火了。嗯这会还是无法配合着，嗯这个压压了点。嗯三三十那考考一个是吧？现在当时王杰传给我的，完了我给你说那时候管什么？18这块是个化肥，现在的场地刚修，啊那会儿工人那会儿就是买啥吃的，别让他往跟前超驱动了。19这块儿嗯刚开始建，现在要是活比较粘着当初的道路，现在换这个电池，呢完了夜班看看管理程度，嗯这战斗的程度这块都管住，管不动的抓紧到人。嗯20转走了也嗯要么就长清完了。嗯主要就是这个早起给这个前场的这个卫生好好搞搞，还有这个人行通道，这个有密这块咱们再清清不清楚，再打扫卫生。其他的没事。从18开始说，吧然后校园宿舍考虑过安全风险管控，十八化肥完了就晚上还给你，然后你那个那个煤矿跟那个18个银行监管一下，16还是10条一条一个，吧最近那个知道很多g的呃嗯嗯嗯嗯你看是有这个马上涉及到消费低头仓和清单的对应，10来平方之前检查好这个吊具抢完，还有民进党是吧？然后吊装式啊工人这个指挥棒对吧？要使用这方面的这个调研事项，嗯小方法，这个安全带我们钱都不够使用，然后他们作业包括人力资力，啊然后都在忙活了，每年调查作业。这个党委里面不是后边年呐战斗数据会包括就是好像电推一样，过去我们看看，吧然后环保方面就是基层干部，包括配合作业的部门这样的事情。嗯20这个是个外贸号，庄里西里翻土应该是船正走着，呢然后我是开班之后已经就办理号牌进靠船的，光是客流不安全这一块，主要是查看工人劳保用品穿戴啊人数配备情况，啊以及人民村b浪啊这个这种全员摆放，这个嗯考虑揽成收益这一块，啊后期这个啊全靠之后联系客户使用场地啊没问题，也不要求正常开发作业，啊开完会之后避免给你叫他做衣服，啊这个铲车啊控制好车，控制好这个车辆秩序，啊这块装车，啊保证这个搞搞效率，啊别的没事。嗯嗯30美嗯同情况嗯已经开工拉了不少了。然后按照这个呃领导们的要求，那个企业上看看他们，他保证夜间后边的时候也聊了啥，司机注意点，然后30年考个140米的船，现在应该是这么考虑的。吧嗯看到那事儿了，假如说晚上能开会就继续放出来，那么嗯打杂工9成一，所以要继续啊继续标准。然后这个九龙穴放到中仓位放到这个我又开始点上，你这是6×46嗯这个贴画上，是找死了，然后然后能弄出去，老能起到这个真正有有用的这个效果。这个30倍吧30倍说是作为速度还比较快，有可能想使用那个小财政，然后加入作业之前把这个铲车的铲头，还有该清的地方都清清，然后上报，并且也是合格的，我想上相当12提前两年，那个我带小伙上了指挥部的一些汇报，那保证它能亮嗯一个指挥还要操作。那里面也是嗯30米那个船正在那个井啊估计8点多能差8:30才能考好吧？啊按照那个取证观众工人那个支撑力，还有就是说方法好看，那个监管之后，嗯咱们上这块现在马上回家来6000吨货，他们摔那个腕子那种，啊那个车队发挥是后半夜差不多能到。啊嗯现在31济钢场地只挤了一半，还有一半青草场上没扫出来，正那个扫着呢嗯扫完楼再验收账，那是我的邮件补偿。嗯你知道那边主要吗？嗯23,000，你现在是几个3000多？吧嗯那我还得督促他们快点上货，也不是18这个一会儿号的时候，洪哥监管一下他这个18仓库的靠泊位置，就隔着往东靠也别往西靠。因为前两天靠泊的时候，研三对咱们班组提出表扬的，就咱们班这个靠的到位，哪个班组靠的都欠缺这个靠泊位置。然后19这个嗯刚才计划组那边通知，呢嗯不需要进车厢的，给那个泵啊政府放到6吨的就不清了。就是带粘着粘着的，一会那个看看这个抖音顺畅度，嗯给那个万利，还有佳能车队都有显著的放倒。一会看看评估一下，看看后期这个倒运的话，如果说后头不压车，给他们车都加上来，嗯这个锰矿这个清仓的，这个就按照正常清仓的流程走就中了。嗯提额外的要求咱们做不到，说少仓乱七八糟的，咱们就不要答应了。这个如果说他需要扫堂干啥，就直接让他找客户经理，啊因为咱不给他扫，因为上个系统这个就涉及到他们锰矿九江的货，要扫仓，后来询问业务部正常流程干净的问题，那20多个10个靠好了之后，要不然重点关注个倒运。然后这个各厂的这个明天早起，然后做好，如果有重点仓的货多的重点做好交接，然后明天早起嗯交接班不停，啊那30北这个通话，按照相关会议纪要要求落实就行了。然后这个后场如果一旦出现堵车了，及时反馈给我，然后联系那个铜金矿那边的一个叫曹总的那个领导，几次打开料槽，人家给咱们的承诺是后场卸车肯定是不会压车，这个看前场的卸车卸货的速度的。这一块晚上呃跟他们车队都说好了，司机让他们交代好，司机只要一单货，东京矿入库发现堵车情况，入库困难的第一时间给我信，然后30这个倒运这一块，然后上班去摸排一下他们有多少车。如果车手的情况下和这一二场数山上流的动，呢如果山上有货的话，后期可以选嗯31还是关注一下这个上货速度。吧然后今天杨山发的那个要求是转达这个安化部那个付海峰科长他们的要求，是要求这个所有的工人下仓了，还有这个干啥那个调到设备这一块也一定要留好记录，要有痕迹，要做好安全告知这一块。然后要不然天气骤降，这个温度比较低，然后这个功能这个身体状态，还有设备司机这个疲劳驾驶这一块控制好了，然后给他们做好安全提醒。其他的没事了。"""

    result = meeting_content_processor.process(test_content)
    print(json.dumps(result, ensure_ascii=False, indent=2))
